{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/obss/BIOBSS/blob/main/examples/ppg_processing.ipynb)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "__BIOBSS - PPG Signal Processing__\n",
    "\n",
    "_This notebook includes guidelines to help using BIOBSS for PPG signal processing and feature extraction._"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "__To run this notebook in Colab:__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "git clone https://github.com/obss/biobss.git\n",
    "cd biobss\n",
    "pip install ."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip install -U matplotlib\n",
    "import shutil\n",
    "from biobss import FIXTURES_ROOT\n",
    "shutil.move(\"/content/biobss/sample_data\",FIXTURES_ROOT.parent)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "__Import required packages:__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import biobss\n",
    "import os\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import pandas as pd"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Table of Contents\n",
    "1. [PPG Sample Data](#sampledata)<br>\n",
    "2. [PPG Signal Preprocessing](#ppg_pre)<br>\n",
    "    2.1. [Filtering](#ppg_filter)<br>\n",
    "    2.2. [Peak Detection](#ppg_peak)<br>\n",
    "    2.3. [Delineation](#ppg_waves)<br>\n",
    "    2.4. [Plotting](#ppg_plot)<br>\n",
    "4. [PPG Signal Quality Assessment](#ppg_sqa)<br>\n",
    "    4.1. [Clipping and Flatline Detection](#ppg_cf)<br>\n",
    "    4.2. [Physiological and Morphological Limits](#ppg_pm)<br>\n",
    "    4.3. [Template Matching](#ppg_tm)<br>\n",
    "    4.4. [Simultaneous SQA Assessment](#sqa_all)<br>\n",
    "5. [PPG Feature Extraction](#ppg_features)<br>\n",
    "6. [VPG and APG Feature Extraction](#vpg_apg_features)<br>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### __PPG Sample Data__\n",
    "<a id=\"sampledata\"></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "PPG sample data is provided as a csv file in BIOBSS\\sample data. The data file contains 100 PPG segments of 10-seconds length. The sampling rate is 64 Hz for all segments."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Load the sample data\n",
    "data, info = biobss.utils.load_sample_data(data_type='PPG_SHORT')\n",
    "sig = np.asarray(data['PPG'])\n",
    "fs = info['sampling_rate']\n",
    "L = info['signal_length']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### __PPG Signal Preprocessing__\n",
    "<a id=\"ppg_pre\"></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### __Filtering__\n",
    "<a id=\"ppg_filter\"></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "BIOBSS provides a filtering function which uses Scipy. This function can be used to implement Butterworth filter by defining the filter parameters (filter type, filter order, cutoff frequencies) as shown below. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Filter PPG signal by defining the filter parameters\n",
    "f_sig= biobss.preprocess.filter_signal(sig,sampling_rate=fs,filter_type='bandpass',N=2,f_lower=0.5,f_upper=5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As an alternative, predefined filters can be used for each signal type. In order to use this option for PPG signal, signal_type should be selected as 'PPG'. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Filter PPG signal by using predefined filters\n",
    "filtered_ppg = biobss.preprocess.filter_signal(sig, sampling_rate=fs, signal_type='PPG', method='bandpass')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### __Peak Detection__\n",
    "<a id=\"ppg_peak\"></a>"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "BIOBSS provides a peak detection function with different alternatives for the peak detection method. These methods are appropriate for PPG signal, however the parameters should be selected properly if the second peak (diastolic peak) is observable in the signal. The ___ppg_detectpeaks___ function returns a dictionary including peak locations and trough locations.\n",
    "\n",
    "For further analysis, a peak control step is required to prevent errors resulting from incorrect peak detection results (missing or dupliciate peaks). The ___peak_control___ function checks the relative locations of peaks and troughs, ensuring only a single peak is located between consecutive troughs. As an option, peak correction procedure can be applied by setting the parameter __correct_peaks__ of ___ppg_detectpeaks___ function as True."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Detect peaks using 'peakdet' method (delta=0.01). Delta parameter should be adjusted related to the amplitude of the signal.\n",
    "\n",
    "info=biobss.ppgtools.ppg_detectpeaks(sig=filtered_ppg, sampling_rate=fs, method='peakdet', delta=0.01, correct_peaks=True)\n",
    "\n",
    "locs_peaks=info['Peak_locs']\n",
    "peaks=sig[locs_peaks]\n",
    "locs_onsets=info['Trough_locs']\n",
    "onsets=sig[locs_onsets]"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In case of PPG waveform has low amplitude, PPG beats can be detected more accurately from the rising edge of the signal, instead of peaks. BIOBSS provides the function ___ppg_detectbeats___ for this purpose. The function calculates the first derivative of the PPG signal (velocity plethysmogram - VPG) and detects peaks of VPG. The maximum peaks in VPG signal corresponds maximum slope point on the rising edge of the PPG signal. Note that, the ___ppg_detectbeats___ function returns only peak locations and __delta__ parameter should be adjusted considering the amplitude of VPG signal."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "locs_peaks=biobss.ppgtools.ppg_detectbeats(sig, sampling_rate=fs, method='peakdet', delta=0.005)\n",
    "peaks=sig[locs_peaks]\n",
    "locs_onsets=info['Trough_locs']\n",
    "onsets=sig[locs_onsets]"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In order to detect beats, ___ppg_detectpeaks___ function can also be used by setting the parameter __type__ as 'beat'. This time, both peak and trough locations are returned."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "info=biobss.ppgtools.ppg_detectpeaks(sig=filtered_ppg, sampling_rate=fs, method='peakdet', delta=0.005, type='beat', correct_peaks=True)\n",
    "\n",
    "locs_peaks=info['Peak_locs']\n",
    "peaks=sig[locs_peaks]\n",
    "locs_onsets=info['Trough_locs']\n",
    "onsets=sig[locs_onsets]"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### __Delineation__\n",
    "<a id=\"ppg_waves\"></a>"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "By definition, PPG signal waveform includes two peaks which are systolic and diastolic peaks however the diastolic peak may not be observable in some conditions. From the diastolic peak locations, some extra features may be calculated. \n",
    "\n",
    "In order to detect the location of diastolic peak, generally the first and second derivatives of PPG signal are needed. First, fiducial points on the first derivative (Velocity Plethysmogram, VPG) and the second derivative (Acceleration Plethysmogram, APG) should be detected. Fiducials can also be used to calculate VPG and APG features which may be helpful in some analysis, e.g. blood pressure estimation from PPG signal. \n",
    "\n",
    "BIOBSS provides delineation functions for PPG, VPG and APG signals which are ___ppg_fiducials___, ___vpg_fiducials___, ___apg_fiducials___ respectively. It is important to note that, the APG delineation function __apg_fiducials__ requires locations of VPG fiducials and the PPG delineation function __ppg_fiducials__ requires locations of VPG and APG fiducials. Thus, the delineation process should follow a certain order. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Calculate first and second derivatives of the PPG signal\n",
    "vpg_sig = np.gradient(filtered_ppg) / (1/fs)\n",
    "apg_sig = np.gradient(vpg_sig) / (1/fs)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The function ___vpg_fiducials___ detects the locations of w, y and z waves in VPG signal and returns a dictionary of fiducials. The function uses a search algorithm, starting from the maximum peak (w-wave) in the signal. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "vpg_fiducials = biobss.ppgtools.vpg_delineate(vpg_sig, sampling_rate=fs)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The function ___apg_fiducials___ detects the locations of a, b, c, d and e waves in APG signal and returns a dictionary of fiducials. The function uses a search algorithm starting from the maximum peak (a-wave) in the signal. It is also required to provide the locations of VPG fiducials since search intervals are defined based on these locations.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "apg_fiducials = biobss.ppgtools.apg_delineate(apg_sig, vpg_sig, vpg_fiducials, sampling_rate=fs)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The function ___ppg_fiducials___ detects the locations of S, N, O and D waves in PPG signal and returns a dictionary of fiducials. The function uses a search algorithm starting from the signal onset locations (O-wave), calculated using peak detection function. It is also required to provide the locations of VPG and APG fiducials since search intervals are defined based on these locations.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ppg_fiducials = biobss.ppgtools.ppg_delineate(filtered_ppg, vpg_sig, vpg_fiducials, apg_sig, apg_fiducials, sampling_rate=fs)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In order to detect all fiducials at one time, the function ___ppg_waves___ can be used."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ppg_waves=biobss.ppgtools.ppg_waves(sig=filtered_ppg, locs_onsets= locs_onsets, sampling_rate=fs)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### __Plotting__\n",
    "<a id=\"ppg_plot\"></a>"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "BIOBSS provides plotting functions specific to each signal type. In order to plot PPG signals, ___plot_ppg___ function can be used. The signals and peaks should be provided as dictionaries, and the keys should be selected appropriately as shown below. The plots can be generated either using __Matplotlib__ or __Plotly__. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Generate inputs as dictionaries\n",
    "signals={'Raw': sig, 'Filtered': f_sig}\n",
    "peaks={'Raw':{'Peaks': locs_peaks, 'Onsets': locs_onsets} , 'Filtered': {'Peaks': locs_peaks, 'Onsets':locs_onsets}}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Plot PPG Signal using Matplotlib\n",
    "biobss.ppgtools.plot_ppg(signals=signals, peaks=peaks, sampling_rate=fs, show_peaks=True, rescale=True, figsize=(10,5))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Plot PPG signal using Plotly\n",
    "biobss.ppgtools.plot_ppg(signals=signals, peaks=peaks, sampling_rate=fs, method='plotly', show_peaks=True, rescale=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### __PPG Signal Quality Assessment__\n",
    "<a id=\"ppg_sqa\"></a>"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "PPG signal is very sensitive to motion artifacts, so signal quality assessment is an important step which may be required prior to signal analysis. For this purpose, rule-based or machine learning based approaches are used based on the morphological information from PPG waveform. BIOBSS provides modules for assessing signal quality, which can be applied seperately or consecutively."
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A clipped and flatline-including version of the PPG sample data can be generated to be used in the following steps. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Generate a modified sample data\n",
    "\n",
    "sig_modified = sig.copy()\n",
    "#Clip the signal\n",
    "sig_modified[sig_modified > 1.01]=1.01\n",
    "sig_modified[sig_modified < 0.99]=0.99\n",
    "sig_modified[125:130] =sig_modified[125]\n",
    "sig_modified[480:490] =sig_modified[480]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Plot the modified signal\n",
    "signals={'Raw': sig_modified}\n",
    "biobss.ppgtools.plot_ppg(signals, method='plotly', show_peaks=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### __Clipping and Flatline Detection__\n",
    "<a id=\"ppg_cf\"></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The ___detect_flatline_clipping___ function is used to detect clipped or flat segments of a signal by setting the parameters and returns a dictionary of boundaries. \n",
    "\n",
    "Clipping occurs because of exceeding voltage limits of the signal conditioning circuits. Clipped segments can be detected by setting a threshold and applying rules on the signal amplitude. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Detect clipped segments of PPG signal by setting a threshold value for signal amplitude\n",
    "clipped_segments=biobss.sqatools.detect_clipped_segments(sig_modified,threshold_pos=1.01,threshold_neg=0.99)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Flatline detection differs from clipping such that it can occur at any level of signal amplitude and duration of flat segments also matters to detect flatlines. Thus, both an amplitude and duration threshold should be defined to apply the rules. Note that, amplitude threshold is defined as the minimum level of amplitude change required for a signal to be considered as non-flat segment."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Detect flat segments of PPG signal by setting thresholds for signal amplitude change and duration of flat segments\n",
    "flatline_segments=biobss.sqatools.detect_flatline_segments(sig_modified,change_threshold=0.000001, min_duration=5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### __Physiological and Morphological Limits__\n",
    "<a id=\"ppg_pm\"></a>"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The ___check_phys___ and ___check_morph___ functions can be used to check for physiological and morphological limits respectively. These functions takes peak and trough locations as arguments and return a dictionary of boolean results of the applied rules.\n",
    "\n",
    "To be able to calculate the morphological features, number of peaks and troughs should match. Thus, peak control procedure should also be applied following peak detection."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Peak detection\n",
    "info=biobss.ppgtools.ppg_detectpeaks(sig=filtered_ppg, sampling_rate=fs, method='peakdet', delta=0.01, correct_peaks=True)\n",
    "\n",
    "locs_peaks=info['Peak_locs']\n",
    "peaks=sig[locs_peaks]\n",
    "locs_onsets=info['Trough_locs']\n",
    "onsets=sig[locs_onsets]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, the phsyiological and morphological limits can be compared to the accepted values in the literature. \n",
    "Reference: https://link.springer.com/book/10.1007/978-3-319-68415-4\n",
    "\n",
    "These values are defined as constants in the corresponding modules as given below. \n",
    "\n",
    "Physiological limits:\n",
    "- HR_MIN = 40 #minimum heart rate\n",
    "- HR_MAX = 180 #maximum heart rate\n",
    "- PP_MAX = 3 #maximum peak to peak interval\n",
    "- MAX_PP_RATIO = 2.2 #maximum P-P interval / minimum P-P interval \n",
    "\n",
    "Morphological limits:\n",
    " \n",
    "- MIN_SPD = 0.08 #minimum systolic phase duration\n",
    "- MAX_SPD = 0.49 #maximum systolic phase duration\n",
    "- SP_DP_RATIO = 1.1 #maximum ratio of systolic to diastolic phase \n",
    "- MIN_PWD = 0.27 #minimum pulse wave duration\n",
    "- MAX_PWD = 2.4 #maximum pulse wave duration\n",
    "- MAX_VAR_DUR = 300 #maximum variation in pulse wave duration (%)\n",
    "- MAX_VAR_AMP = 400 #maximum variation in pulse wave amplitude (%)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Check for physiological and morphological limits \n",
    "info_phys=biobss.sqatools.check_phys(locs_peaks,fs)\n",
    "info_morph=biobss.sqatools.check_morph(sig,locs_peaks,locs_onsets,fs)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### __Template Matching__\n",
    "<a id=\"ppg_tm\"></a>"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A common method for signal quality assessment is the one known as \"Template Matching\". This method is based on the expectation that pulse waveforms will have similar morphology in a PPG segment. A template is generated by aligning the pulses by their peaks and averaging them. Then, similarity of each pulse with the template is assessed using a measure. The ___template_matching___ function uses Pearson correlation as similarity measure. A threshold should be determined for correlation coefficient below which the pulse is \"unacceptable\". The default value is set as 0.9 for the threshold. The function returns a tuple of correlation coefficients and a boolean result for the quality of the segment."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Template matching \n",
    "info=biobss.sqatools.template_matching(sig,locs_peaks)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### __Simultaneous SQA Assessment__\n",
    "<a id=\"sqa_all\"></a>"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The methods defined above can also be applied simultaneously using the function ___sqa_ppg___ from __ppgtools__ subpackage. Methods can be provided as a list and the function returns a dictionary of results. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "info=biobss.ppgtools.sqa_ppg(ppg_sig=sig, sampling_rate=fs, methods=['clipping', 'flatline','physiological','morphological','template'], change_threshold=0.000001, threshold_pos=1.01,threshold_neg=0.99, min_duration=5, peaks_locs=locs_peaks, troughs_locs=locs_onsets)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### __PPG Feature Extraction__\n",
    "<a id=\"ppg_features\"></a>"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "PPG signal is mostly used for heart rate calculation and heart rate variability analysis. However it can also be used to estimate physiological parameters such as respiration rate and blood pressure. There are several different approaches for estimation and one of them is to use machine learning models. Generally morphological / time domain, frequency domain and statistical features are used to train the machine learning models. BIOBSS has modules for calculation of the most common features in the literature. "
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<b>Time domain / morphological features</b>:\n",
    "\n",
    "<u>Cycle-based features</u>:\n",
    "- a_S: Mean amplitude of the systolic peaks \n",
    "- t_S: Mean systolic peak duration\n",
    "- t_C: Mean cycle duration\n",
    "- DW: Mean diastolic peak duration\n",
    "- SW_10: The systolic peak duration at 10% amplitude of systolic amplitude\n",
    "- SW_25: The systolic peak duration at 25% amplitude of systolic amplitude\n",
    "- SW_33: The systolic peak duration at 33% amplitude of systolic amplitude\n",
    "- SW_50: The systolic peak duration at 50% amplitude of systolic amplitude\n",
    "- SW_66: The systolic peak duration at 66% amplitude of systolic amplitude\n",
    "- SW_75: The systolic peak duration at 75% amplitude of systolic amplitude\n",
    "- DW_10: The diastolic peak duration at 10% amplitude of systolic amplitude\n",
    "- DW_25: The diastolic peak duration at 25% amplitude of systolic amplitude\n",
    "- DW_33: The diastolic peak duration at 33% amplitude of systolic amplitude\n",
    "- DW_50: The diastolic peak duration at 50% amplitude of systolic amplitude\n",
    "- DW_66: The diastolic peak duration at 66% amplitude of systolic amplitude\n",
    "- DW_75: The diastolic peak duration at 75% amplitude of systolic amplitude\n",
    "- DW_SW_10: The ratio of diastolic peak duration to systolic peak duration at 10% amplitude of systolic amplitude\n",
    "- DW_SW_25: The ratio of diastolic peak duration to systolic peak duration at 25% amplitude of systolic amplitude\n",
    "- DW_SW_33: The ratio of diastolic peak duration to systolic peak duration at 33% amplitude of systolic amplitude\n",
    "- DW_SW_50: The ratio of diastolic peak duration to systolic peak duration at 50% amplitude of systolic amplitude\n",
    "- DW_SW_66: The ratio of diastolic peak duration to systolic peak duration at 66% amplitude of systolic amplitude\n",
    "- DW_SW_75: The ratio of diastolic peak duration to systolic peak duration at 75% amplitude of systolic amplitude\n",
    "- PR_mean: Mean pulse rate\n",
    "- a_D: Mean amplitude of the diastolic peaks \n",
    "- t_D: Mean difference between diastolic peak and onset\n",
    "- r_D: Mean ratio of the diastolic peak amplitude to diastolic peak duration\n",
    "- a_N: Mean amplitude of the dicrotic notchs\n",
    "- t_N: Mean dicrotic notch duration\n",
    "- r_N: Mean ratio of the dicrotic notch amplitude to dicrotic notch duration\n",
    "- dT: Mean duration from systolic to diastolic peaks\n",
    "- r_D_NC: Mean ratio of diastolic peak amplitudes to difference between ppg wave duration and dictoric notch duration\n",
    "- r_N_NC: Mean ratio of dicrotic notch amplitudes to difference between ppg wave duration and dictoric notch duration\n",
    "- a_N_S: Mean ratio of dicrotic notch amplitudes to systolic peak amplitudes\n",
    "- AI: Mean ratio of diastolic peak amplitudes to systolic peak amplitudes\n",
    "- AI_2: Mean ratio of difference between systolic and diastolic peak amplitudes to systolic peak amplitudes\n",
    "\n",
    "\n",
    "<u>Segment-based features</u>:\n",
    "- zcr: Zero crossing rate\n",
    "- snr: Signal to noise ratio\n",
    "\n",
    "<b>Frequency domain features</b>:\n",
    "\n",
    "<u>Segment-based features</u>:\n",
    "\n",
    "- p_1: The amplitude of the first peak from the fft of the signal\n",
    "- f_1: The frequency at which the first peak from the fft of the signal occurred\n",
    "- p_2: The amplitude of the second peak from the fft of the signal\n",
    "- f_2: The frequency at which the second peak from the fft of the signal occurred\n",
    "- p_3: The amplitude of the third peak from the fft of the signal\n",
    "- f_3: The frequency at which the third peak from the fft of the signal occurred\n",
    "- pow: Power of the signal at a given range of frequencies\n",
    "- rpow: Ratio of the powers of the signal at given ranges of frequencies\n",
    "\n",
    "<b>Statistical features</b>:\n",
    "\n",
    "<u>Cycle-based features</u>: \n",
    "- mean_peaks: Mean of the peak amplitudes\n",
    "- std_peaks: Standard deviation of the peak amplitudes\n",
    "\n",
    "<u>Segment-based features</u>:\n",
    "    \n",
    "- mean: Mean value of the signal\n",
    "- median: Median value of the signal\n",
    "- std: Standard deviation of the signal\n",
    "- pct_25: 25th percentile of the signal\n",
    "- pct_75 75th percentile of the signal\n",
    "- mad: Mean absolute deviation of the signal\n",
    "- skewness: Skewness of the signal\n",
    "- kurtosis: Kurtosis of the signal\n",
    "- entropy: Entropy of the signal"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "These features can be calculated seperately for each domain using the functions ___get_time_features___, ___get_freq_features___ and ___get_stat_features___. These functions requires a parameter 'type' to select for the feature subset. Cycle-based features are calculated for each cycle (waveform) and averaged for all cycles in a segment. Segment-based features are calculated for the whole segment. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Calculate cycle-based time-domain features\n",
    "ppg_time = biobss.ppgtools.ppg_features.ppg_time_features(filtered_ppg, sampling_rate=fs, input_types=['cycle','segment'], peaks_locs=locs_peaks, troughs_locs=locs_onsets)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If fiducials are provided, features are expanded by calculating the D and N wave related features. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ppg_time = biobss.ppgtools.ppg_features.ppg_time_features(filtered_ppg, sampling_rate=fs, input_types=['cycle','segment'], fiducials=ppg_fiducials) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Calculate frequency-domain features\n",
    "ppg_freq = biobss.ppgtools.ppg_features.ppg_freq_features(sig, sampling_rate=fs, input_types=['segment'])\n",
    "\n",
    "#Calculate cycle-based statistical features\n",
    "ppg_stat = biobss.ppgtools.ppg_features.ppg_stat_features(sig, sampling_rate=fs, input_types=['segment'], peaks_amp=peaks, peaks_locs=locs_peaks, troughs_locs=locs_onsets)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As an alternative, ___from_cyles___ and ___from_segment___ functions can be used to calculate features of multiple domains. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Calculate cycle-based features for all valid domains\n",
    "ppg_cycle=biobss.ppgtools.ppg_features.from_cycles(sig=sig,peaks_locs=locs_peaks,troughs_locs=locs_onsets,sampling_rate=fs,feature_types=['Time','Stat'],fiducials=ppg_fiducials,prefix='ppg')\n",
    "\n",
    "#Calculate segment-based features for all valid domains\n",
    "ppg_segment=biobss.ppgtools.ppg_features.from_segment(sig=sig,sampling_rate=fs,feature_types=['Time','Stat','Freq'], prefix='ppg')"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The function ___get_ppg_features___ can be used to calculate features of multiple domains and input_types at one time."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "features_all = biobss.ppgtools.get_ppg_features(sig, sampling_rate=fs, input_types=['cycle','segment'], feature_domain={'cycle':['Time'],'segment':['time','freq','stat']}, peaks_locs=locs_peaks, peaks_amp=peaks, troughs_locs=locs_onsets, troughs_amp=onsets)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### __VPG and APG Feature Extraction__\n",
    "<a id=\"vpg_apg_features\"></a>"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<b>VPG features</b>:\n",
    "- a_w: Mean amplitude of w waves \n",
    "- t_w: Mean duration of w waves \n",
    "- a_y: Mean amplitude of y waves \n",
    "- t_y: Mean duration of y waves \n",
    "- a_z: Mean amplitude of z waves \n",
    "- t_z: Mean duration of z waves \n",
    "- a_y_w: Mean ratio of y wave amplitudes to w wave amplitudes\n",
    "\n",
    "<b>APG features</b>:\n",
    "- a_a: Mean amplitude of a waves \n",
    "- t_a: Mean duration of a waves \n",
    "- a_b: Mean amplitude of b waves \n",
    "- t_b: Mean duration of b waves \n",
    "- a_c: Mean amplitude of c waves \n",
    "- t_c: Mean duration of c waves \n",
    "- a_d: Mean amplitude of d waves \n",
    "- t_d: Mean duration of d waves \n",
    "- a_e: Mean amplitude of e waves \n",
    "- t_e: Mean duration of e waves \n",
    "- a_b_a: Mean ratio of b wave amplitude to a wave amplitude\n",
    "- a_c_a: Mean ratio of c wave amplitude to a wave amplitude\n",
    "- a_d_a: Mean ratio of d wave amplitude to a wave amplitude\n",
    "- a_e_a: Mean ratio of e wave amplitude to a wave amplitude\n",
    "- a_cdb_a: Mean ratio of a_c + a_d - a_b to a wave amplitude\n",
    "- a_bcde_a: Mean ratio of a_b - a_c - a_d - a_e to a wave amplitude\n",
    "- a_bcd_a: Mean ratio of a_b - a_c - a_d to a wave amplitude\n",
    "- a_be_a: Mean ratio of a_b - a_e to a wave amplitude\n"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The functions ___get_vpg_features___ and ___get_apg_features___ can be used to calculate VPG and APG features respectively."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "features_vpg = biobss.ppgtools.get_vpg_features(vpg_sig=vpg_sig, locs_O=locs_onsets, fiducials=vpg_fiducials, sampling_rate=fs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "features_apg = biobss.ppgtools.get_apg_features(apg_sig=vpg_sig, locs_O=locs_onsets, fiducials=apg_fiducials, sampling_rate=fs)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.8.5 64-bit ('biolib')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.16"
  },
  "orig_nbformat": 4,
  "vscode": {
   "interpreter": {
    "hash": "3a4bcfb23c7e6ad66c655087280fa9f4d0273121ae7909f7735d1e02563a2438"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
